package cz.tomasdvorak.cars.dao.impl;

import cz.tomasdvorak.cars.dao.ExpensesDao;
import cz.tomasdvorak.cars.dao.UsersDao;
import cz.tomasdvorak.cars.dao.VehiclesDao;
import cz.tomasdvorak.cars.dto.Expense;
import cz.tomasdvorak.cars.dto.ExpenseCriteria;
import cz.tomasdvorak.cars.dto.ExpenseType;
import cz.tomasdvorak.cars.utils.Lists;
import cz.tomasdvorak.cars.auth.MyAuthenticatedWebSession;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.jdbc.core.simple.SimpleJdbcTemplate;
import org.apache.wicket.Session;

import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;

// TODO: remove table names from code and move to config xml
public class ExpenseDaoImpl implements ExpensesDao {

    private DataSource dataSource;
    private SimpleJdbcTemplate jdbcTemplate;
    private UsersDao usersDao;
    private VehiclesDao vehiclesDao;

    private final RowMapper<Expense> EXPENSE_MAPPER = new RowMapper<Expense>() {
        public Expense mapRow(ResultSet rs, int i) throws SQLException {
            String typeString = rs.getString("expenseType");
            ExpenseType type = null;
            if (typeString != null && !typeString.trim().isEmpty()) {
                type = ExpenseType.valueOf(typeString);
            }
            long dateValue = rs.getLong("expenseDate");
            Date date = dateValue > 0 ? new Date(dateValue) : null;
            return new Expense(rs.getInt("id"), type, date,
                    rs.getInt("mileage"), rs.getInt("quantity"), rs.getInt("price"), rs.getInt(
                            "personId"), rs.getInt("vehicleId"));
        }
    };

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
        jdbcTemplate = new SimpleJdbcTemplate(dataSource);
    }

    public void setUsersDao(UsersDao usersDao) {
        this.usersDao = usersDao;
    }

    public void setVehiclesDao(VehiclesDao vehiclesDao) {
        this.vehiclesDao = vehiclesDao;
    }

    public void insertOrUpdate(Expense expense) {
        if(expense.isNew()) {
            insert(expense);
        } else {
            update(expense);
        }
    }

    private void update(Expense expense) {
        if (expense.getPersonId() == -1) {
            MyAuthenticatedWebSession session = (MyAuthenticatedWebSession) Session.get();
            expense.setPersonId(session.getUser().getId());
        }
        jdbcTemplate.update("UPDATE tdvorak_expense SET expenseType=?, expenseDate=?, mileage=?, quantity=?, price=?, personId=?, vehicleId=? WHERE id=?",
                expense.getType() != null ? expense.getType().name() : "",
                expense.getDate().getTime(),
                expense.getMileage(),
                expense.getQuantity(),
                expense.getPrice(),
                expense.getPersonId(),
                expense.getVehicleId(),
                expense.getId()
        );
    }

    private void insert(Expense expense) {
        if (expense.getPersonId() == -1) {
            MyAuthenticatedWebSession session = (MyAuthenticatedWebSession) Session.get();
            expense.setPersonId(session.getUser().getId());
        }
        jdbcTemplate.update("INSERT INTO tdvorak_ expense (expenseType, expenseDate, mileage, quantity, price, personId, vehicleId) VALUES(?,?,?,?,?,?,?)",
                expense.getType() != null ? expense.getType().name() : "",
                expense.getDate().getTime(),
                expense.getMileage(),
                expense.getQuantity(),
                expense.getPrice(),
                expense.getPersonId(),
                expense.getVehicleId()
        );
    }

    public List<Expense> getAll(ExpenseCriteria criteria) {
        List<Expense> all = jdbcTemplate.query("SELECT * FROM tdvorak_expense", EXPENSE_MAPPER);
        return filterResults(criteria, all);
    }

    /**
     * Is generally not good idea to filter results in application, much better is fiter them in db,
     * but this is much easier. I beleave that for basic usage is it ok, in heavy usage will be
     * needed to rewrite it.
     */
    private List<Expense> filterResults(ExpenseCriteria criteria, List<Expense> all) {
        List<Expense> result = Lists.arrayList();
        for(Expense expense : all) {
            if(criteria.getVehicle() != null) {
                if(expense.getVehicleId() != criteria.getVehicle()) {
                    continue;
                }
            }
            if(criteria.getDateFrom() != null) {
                if(expense.getDate().before(criteria.getDateFrom())) {
                    continue;
                }
            }

            if(criteria.getDateTill() != null) {
                if(expense.getDate().after(criteria.getDateTill())) {
                    continue;
                }
            }

            if(criteria.getExpenseType() != null) {
                if(expense.getType() != criteria.getExpenseType()) {
                    continue;
                }
            }
            result.add(expense);
        }
        return result;
    }
}
